#ifndef BASE_APP_HPP
#define BASE_APP_HPP

#include <GLFW/glfw3.h>
#include <gl/gl_validation.hpp>
#include <imgui/imgui.h>
#include <imgui/imgui_internal.h>
#include <imgui/imgui_stdlib.h>
#include <imgui/imgui_ext.hpp>
#include <imgui/IconsFontAwesome4.h>

class App {
public:
    enum UI_FLAG_ENUM {
        UF_CONFIG  = 1,
        UF_ALONE   = 2,
        UF_ALL     = 0xf,
        UF_NONE    = 0,
    };

    explicit App(GLFWwindow *window, const char *name);
    virtual ~App();

    // life circle
    virtual void OnInitialize();
    virtual void OnCleanup();

    void Update();

    void Exit();
    // events
protected:
    virtual void OnWindowResized(int w, int h);
    virtual void OnWindowMoved(int x, int y);
    virtual void OnKey(int key, int modifiers, bool is_press);
    virtual void OnMouseClicked(int button, int mods, bool is_press);
    virtual void OnWheelScroll(int dx, int dy);
    virtual void OnMouseMove(double x, double y);

    // override by client
    virtual void RenderFrame(double delta_time, double total_time) = 0;
    virtual void RenderConfigUI();
    virtual void StandaloneUI();
    virtual uint32_t UIFlags() const;

protected:
    // internal states
    GLFWwindow *m_window_;
    double m_time_;
    double m_frame_time_;
    double m_time_offset_;
    int m_window_width_;
    int m_window_height_;
    bool m_need_resize_viewport_;

    friend class Callbacks;

private:
    bool m_config_ui_enabled_;
    bool m_vsync_enabled_;
    glm::vec4 m_clear_color_;

    void ImGuiInit();
    void InitFonts();
    void ImGuiCleanup();
    void RenderGui();
};

using UniqueApp = std::unique_ptr<App>;
using PFN_CreateApp = UniqueApp (*)(GLFWwindow *window, const std::vector<std::string> &args);

void RegisterAppCreate(const char *name, PFN_CreateApp factory);
UniqueApp CreateApp(const std::string &name, GLFWwindow *window, const std::vector<std::string> &args);

#define CONCAT(x, y) x##y
#define REGISTER_APP_CREATOR_IMPL(TYPENAME, VAR_NAME, NAME, PROC) \
    static struct TYPENAME {                                      \
        TYPENAME(const char *name, PFN_CreateApp factory) {       \
            RegisterAppCreate(name, factory);                     \
        }                                                         \
    } VAR_NAME(NAME, PROC);

#define REGISTER_APP_CREATOR(NAME, PROC) REGISTER_APP_CREATOR_IMPL(CONCAT(AppRegister, __LINE__), CONCAT(Register_, __LINE__), NAME, PROC)

#define DEFINE_FACTORY_FUNC(func, p1, p2) \
    static UniqueApp func(GLFWwindow *p1, const std::vector<std::string> &p2)

#endif// BASE_APP_HPP
